﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using Linq2Crm;
using Linq2Crm.ObjectModel;
using Linq2Crm.ObjectModel.DomainObject;
using Linq2Crm.Request;
using Linq2Crm.Request.SpecificRequest;
using SLApp_Test.Model;

namespace SLApp_Test
{
   public partial class ViewModel
   {
      Random rnd = new Random();
      private string uniqueName = "KAWABANGA";
      private int bookingCount = 190;
      private int assignmentCount = 1;
      private int projectCount = 1;
      List<BaseEntity> testData = new List<BaseEntity>();
      private BookingType billableBookingType;
      private User user;
      private Employee employee;

      private async Task CreateInvoiceTestData()
      {
         DateTime beginOfThisMonth = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);

         #region employee and user

         user = await GetUser();

         employee = await GetEmployee(user.Id);
         if (employee == null)
         {
            throw new Exception("Cannot find employee");
         }
         #endregion

         List<BaseEntity> availableCurrencies;
         #region currency
         using (Messager.GetNew("Getting currencies"))
         {
            GetRequest getCurrenciesRequest = GetRequest.Create<Currency>();
            getCurrenciesRequest.ReportProgress = new Progress<int>(ProgressReporter);
            getCurrenciesRequest.Query = GetRequest.CreateQuery<Currency>().Where(x => x.StateCode == RecordStateCode.Active);
            GetResponse currencies = (GetResponse)(await DomainObjectContext.Instance.ExecuteAsync(getCurrenciesRequest));
            Messager.WriteStatus(currencies);
            availableCurrencies = currencies.Items.Cast<BaseEntity>().ToList();
         }
         #endregion

         #region booking types

         using (Messager.GetNew("Getting booking types"))
         {
            GetRequest getBookingTypes = GetRequest.Create<BookingType>();
            getBookingTypes.ReportProgress = new Progress<int>(ProgressReporter);
            getBookingTypes.Query = GetRequest.CreateQuery<BookingType>().Where(x => x.StateCode == RecordStateCode.Active);
            GetResponse bookingTypes = (GetResponse)(await DomainObjectContext.Instance.ExecuteAsync(getBookingTypes));
            Messager.WriteStatus(bookingTypes);
            billableBookingType = (BookingType)bookingTypes.Items.FirstOrDefault(x => ((BookingType)x).ActivityrecordType == activityrecordtype.billable);
         }
         #endregion

         Account testAccount = new Account();
         #region Account
         testAccount.Currency = availableCurrencies[rnd.Next(0, availableCurrencies.Count - 1)].ToReference();
         testAccount.Name = uniqueName + " ACCOUNT TEST";

         using (Messager.GetNew("Add test account"))
         {
            InsertRequest ir = InsertRequest.Create<Account>(testAccount);
            InsertResponse insResponse = (InsertResponse)(await DomainObjectContext.Instance.ExecuteAsync(ir));
            Messager.WriteStatus(insResponse);
         }

         testData.Add(testAccount);
         #endregion

         for (int i = 0; i < projectCount; i++)
         {
            BusyContent = string.Format("Creating {0}-{1}-{2}", i + 1, 0, 0);
            Project project = null;
            using (Messager.GetNew("Create project"))
            {
               project = new Project()
               {
                  Currency = availableCurrencies[rnd.Next(0, availableCurrencies.Count - 1)].ToReference(),
                  Name = (i + 1).ToString() + " " + uniqueName + "project",
                  Customer = testAccount.ToReference(),
                  BookingType = bookingtype.TM,
                  DefaultBookingType = billableBookingType.ToReference(),
                  ProjectManager = employee.ToReference(),
                  Start = beginOfThisMonth,
                  EstimatedEffort = rnd.Next(10, 100)
               };

               Messager.WriteStatus(await DomainObjectContext.Instance.ExecuteAsync(InsertRequest.Create<Project>(project)));
            }
            testData.Add(project);
            for (int j = 0; j < assignmentCount; j++)
            {
               BusyContent = string.Format("Creating {0}-{1}-{2}", i + 1, j + 1, 0);
               ProjectAssignment assignment = null;
               using (Messager.GetNew("Create assignment"))
               {
                  assignment = new ProjectAssignment()
                     {
                        Currency = availableCurrencies[rnd.Next(0, availableCurrencies.Count - 1)].ToReference(),
                        Name = (i + 1).ToString() + "." + (j + 1).ToString() + " " + uniqueName + " assignment",
                        HourlyRate = new decimal(rnd.Next(3)),
                        Project = project.ToReference(),
                        Start = beginOfThisMonth.AddDays(j),
                        Employee = employee.ToReference(),
                     };
                  Messager.WriteStatus(await DomainObjectContext.Instance.ExecuteAsync(InsertRequest.Create<ProjectAssignment>(assignment)));
               }
               testData.Add(assignment);

               for (int k = 0; k < bookingCount; k++)
               {
                  BusyContent = string.Format("Creating {0}-{1}-{2}", i + 1, j + 1, k + 1);
                  Booking booking = null;
                  using (Messager.GetNew("Create booking"))
                  {
                     var begin = beginOfThisMonth.AddDays(j).AddDays(rnd.Next(10));
                     int hours = rnd.Next(10);
                     booking = new Booking()
                        {
                           BookingState = bookingstate.approved,
                           Currency = availableCurrencies[rnd.Next(0, availableCurrencies.Count - 1)].Id,
                           Hourlyrate = rnd.Next(10, 30),
                           Name = (i + 1).ToString() + "." + (j + 1).ToString() + "." + (k + 1).ToString() + " " + uniqueName + " booking",
                           BookingType = bookingtype.TM,
                           BookingTypeId = billableBookingType.Id,
                           ProjectAssignmentId = assignment.Id,
                           ProjectId = project.Id,
                           StartTime = begin,
                           EndTime = begin.AddHours(hours),
                           Duration = hours
                        };

                     Messager.WriteStatus(await DomainObjectContext.Instance.ExecuteAsync(InsertRequest.Create<Booking>(booking)));
                  }
                  testData.Add(booking);
               }
            }
         }
      }

      private async Task<Employee> GetEmployee(Guid userId)
      {
         using (Messager.GetNew("Get employee"))
         {
            GetRequest getEmployee = GetRequest.Create<Employee>();
            getEmployee.Query = GetRequest.CreateQuery<Employee>().Where(x => x.SystemUserId == userId);

            GetResponse emplResponse = (GetResponse)(await DomainObjectContext.Instance.ExecuteAsync(getEmployee));
            Messager.WriteStatus(emplResponse);
            return (Employee)emplResponse.Items.FirstOrDefault();
         }
      }

      private async Task<User> GetUser()
      {
         using (Messager.GetNew("Get user"))
         {
            WhoAmIRequestWrapper whoAmIRequest = WhoAmIRequestWrapper.Create();

            var wrapperResp = (WhoAmIResponseWrapper)await DomainObjectContext.Instance.ExecuteAsync(whoAmIRequest);
            return new User()
               {
                  Id = wrapperResp.UserId
               };
         }
      }

      private async Task CleanUpInvoiceTestData()
      {
         var user = await GetUser();
         var empl = await GetEmployee(user.Id);

         var projAssignments = new List<ProjectAssignment>();
         using (Messager.GetNew("Getting assignment"))
         {
            GetRequest gr = GetRequest.Create<ProjectAssignment>();
            gr.ReportProgress = new Progress<int>(ProgressReporter);
            gr.Query = GetRequest.CreateQuery<ProjectAssignment>().Where(x => x.Employee.Id == empl.Id);
            GetResponse assignments = (GetResponse)(await DomainObjectContext.Instance.ExecuteAsync(gr));
            Messager.WriteStatus(assignments);
            projAssignments.AddRange(assignments.Items.Cast<ProjectAssignment>());
         }
         testData.AddRange(projAssignments.Where(z=>testData.Any(x=>x.Id != z.Id)));

         var clearingItems = new List<ServiceItem>();
         var projAssIds = projAssignments.Select(x => x.Id).ToArray();
         using (Messager.GetNew("Getting assignment"))
         {
            GetRequest gr = GetRequest.Create<ServiceItem>();
            gr.ReportProgress = new Progress<int>(ProgressReporter);
            gr.Query = GetRequest.CreateQuery<ServiceItem>().Where(x => projAssIds.Contains(x.ProjectAssignment.Id));
            GetResponse clItems = (GetResponse)(await DomainObjectContext.Instance.ExecuteAsync(gr));
            Messager.WriteStatus(clItems);
            clearingItems.AddRange(clItems.Items.Cast<ServiceItem>());
         }
         testData.AddRange(clearingItems.Where(z => testData.Any(x => x.Id != z.Id)));

         var books = new List<Booking>();
         using (Messager.GetNew("Getting bookings"))
         {
            GetRequest gr2 = GetRequest.Create<Booking>();
            gr2.ReportProgress = new Progress<int>(ProgressReporter);
            Guid[] ids = projAssignments.Select(x => x.Id).ToArray();
            gr2.Query = GetRequest.CreateQuery<Booking>().Where(x => ids.Contains(x.ProjectAssignmentId));
            GetResponse bookings = (GetResponse)(await DomainObjectContext.Instance.ExecuteAsync(gr2));
            Messager.WriteStatus(bookings);
            books.AddRange(bookings.Items.Cast<Booking>());
         }
         testData.AddRange(books.Where(z => testData.Any(x => x.Id != z.Id)));

         testData.Reverse();

         for (int index = 0; index < testData.Count; index++)
         {
            var entity = testData[index];
            BusyContent = string.Format("Deleting {0} of {1}", index + 1, testData.Count);
            using (Messager.GetNew("Delete " + entity.SchemaName + " - " + entity.Name))
            {
               try
               {
                  Messager.WriteStatus(await DomainObjectContext.Instance.ExecuteAsync(DeleteRequest.Create<BaseEntity>(entity)));
               }
               catch (Exception e)
               {
                  Messager.AddLine(e.Message);
               }
            }
         }

         testData.Clear();
      }

      private void ProgressReporter(int i)
      {
         Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
               BusyContent = string.Format("Progress: {0}%", i);
            });
      }
   }

   internal class IDComparer : IEqualityComparer<BaseEntity>
   {
      #region Implementation of IEqualityComparer<in BaseEntity>
      public bool Equals(BaseEntity x, BaseEntity y)
      {
         return x.Id == y.Id;
      }

      public int GetHashCode(BaseEntity obj)
      {
         return obj.Id.GetHashCode();
      }
      #endregion
   }
}